#pragma once
#include <initializer_list>
#include <iostream>
#include <array>
using namespace std;

template<typename T> class MyQueue
{
	T* m_p{}; // ��������� �� ������ ������������� �������
	size_t m_n{}; // ���������� ���������� ��������� � �������
	size_t m_cap{ 1 }; // ������� (������� �������� ������)
	size_t m_first{}; // ������ ������� �������� � ������� (��� ��� �������, �������
	// ����� ������� �� ������� � ������� pop())
	size_t m_last{}; // ������ ������� ���������� �������� � ������� (��� ��� �������, �
	// ������� ����� ��������� ����� �������� � ������� push())
	static const size_t delta{ 2 }; // �� ������� ����������� ������� ��� ����������������� ������
public:
	class iterator {
		// ������ � ������, ����������� ���������������� ��������� ��� ��������� �������
		const MyQueue* m_pQ; // ����������� �������
		int m_i; // ������ �������� ��������
	public:
		iterator(const MyQueue* x, size_t ind = 0) : m_pQ(x), m_i(ind) {}
		// ��������� ������, ������� ���������� range-based for
		iterator& operator++ () { m_i = ++m_i % m_pQ->m_cap; return *this; } // �������� ����������� ����������
		T& operator* () { return m_pQ->m_p[m_i]; } // �������� �������������
		bool operator!= (const iterator& other) const { return other.m_i != this->m_i; } // �������� ���������
		/*int operator-(const iterator& other)const { return abs(m_first - m_last); }*/
		// ����� ���� ����������� ���-�� ���
		//�
		typedef T         value_type;
		typedef ptrdiff_t  difference_type;
		typedef T*   pointer;
		typedef T& reference;
		typedef std::input_iterator_tag  iterator_category;
	};
	iterator begin() const { return MyQueue::iterator(this, m_first); } // �������� �� ������ �������
	iterator end() const { return MyQueue::iterator(this, m_last); } // �������� �� ����� �������

	// ��� ��� ����� �������, ��������� ��������������� �����:
	MyQueue() = default;/*:m_p(nullptr) {}*/
	~MyQueue() { delete[] m_p; m_cap = 1; m_n = m_first = m_last = 0;}

	MyQueue(size_t, T&&);
	MyQueue(initializer_list<T>);
	MyQueue(const MyQueue&);
	MyQueue(MyQueue&&) noexcept;

	MyQueue& operator= (const MyQueue&);
	MyQueue& operator= (MyQueue&&) noexcept;
	MyQueue& operator= (initializer_list<T>);
	//�
	// ������, ����������� ���������������� �������
	void push(const T& val);
	void push(T&& val);
	T pop();

	// � �����, ������, ����������� ��� ���������� �������
	//�
	void print();
};

template<typename T>
inline MyQueue<T>::MyQueue(size_t x, T&& val)
	:m_cap(x + 1), m_n(x), m_last(x)
{
	m_p = new T[m_cap]{ move(val) };//��� ������������������� ��� ��������?
	for (size_t i = 1; i < m_n; i ++ )
	{
		m_p[i] = m_p[0];
	}
}

template<typename T>
inline MyQueue<T>::MyQueue(initializer_list<T>l)
	:m_cap(l.size()+ 1), m_n(l.size())
{
	m_last = l.size();
	m_p = new T[m_cap];
	copy(l.begin(),l.end(), m_p);
}

template<typename T>
inline MyQueue<T>::MyQueue(const MyQueue& other)
	:m_n(other.m_n), m_cap(other.m_n + delta), m_last(other.m_n)
{
	m_p = new T[m_cap];
	copy(other.begin(), other.end(), m_p);
	/*for (const auto& x : other)
	{
		m_p[m_last++] = x;
	}*/
}

template<typename T>
inline MyQueue<T>::MyQueue(MyQueue&& other) noexcept
	:m_p(other.m_p), m_n(other.m_n), m_cap(other.m_cap), m_first(other.m_first), m_last(other.m_last)
{
	other.m_p = nullptr;
	other.m_cap = 1;
	other.m_n = other.m_first = other.m_last = 0;
}

template<typename T>
inline MyQueue<T>& MyQueue<T>::operator=(const MyQueue& other)
{
	if (this != &other)
	{
		if (m_cap <= other.m_n)
		{
			delete[] m_p;
			m_cap = other.m_n + delta;
			m_p = new T[m_cap];
		}

		m_n = other.m_n;
		/*m_last = */m_first = 0;
		copy(other.begin(), other.end(), m_p); m_last = (m_first + m_n) % m_cap;
		/*for (const auto& x : other)
		{
			m_p[m_last++] = x;
		}*/
	}
	return *this;
}

template<typename T>
inline MyQueue<T>& MyQueue<T>::operator=(MyQueue&& other) noexcept
{
	if (this != &other)
	{
		delete[] m_p;

		m_p = other.m_p;
		m_n = other.m_n;
		m_cap = other.m_cap;
		m_first = other.m_first;
		m_last = other.m_last;
		
		other.m_p = nullptr;
		other.m_cap = 1;
		other.m_n = other.m_first = other.m_last = 0;
	}
	return *this;
}

template<typename T>
inline MyQueue<T>& MyQueue<T>::operator=(initializer_list<T> l)
{
	if (m_cap <= l.size())
	{
		m_cap = l.size() + delta;
		delete[] m_p;
		m_p = new T[m_cap];
	}
	
	m_n = l.size();
	m_first = 0;
	m_last = l.size();
	
	copy(l.begin(), l.end(), m_p);

	return *this;
}

template<typename T>
inline void MyQueue<T>::push(const T& val)
{
	if (m_n == m_cap-1)
	{
		m_cap += delta; 
		T* tmp = new T[m_cap];

		copy(make_move_iterator(m_p), move_iterator(m_p + m_n), tmp);//move_iterator<???> ����� ������ ��� ���� � � ��� ������� �� make_move_iterator

		delete[] m_p;
		m_p = tmp;		
	}	
		m_p[m_last] = val;
		m_n++;
		m_last = (m_first + m_n) % m_cap;				
}

template<typename T>
inline void MyQueue<T>::push(T&& val)
{
	if (m_n == m_cap - 1)
	{
		m_cap += delta;
		T* tmp = new T[m_cap];

		copy(make_move_iterator(m_p), move_iterator(m_p + m_n), tmp);//move_iterator<???> ����� ������ ��� ���� � � ��� ������� �� make_move_iterator

		delete[] m_p;
		m_p = tmp;
	}
	m_p[m_last] = move(val);
	m_n++;
	m_last = (m_first + m_n) % m_cap;
}

template<typename T>
inline T MyQueue<T>::pop()
{
	T tmp = m_p[m_first];
	m_n--;
	m_first = (m_first + 1) % m_cap;
	return tmp;
}

template<typename T>
inline void MyQueue<T>::print()
{
	if (m_cap == 1) { cout << "Empty queue"; }
	else 
	{
		for (size_t i = 0; i < m_cap; i++)
		{
			cout << "index:" << i << " value: " << m_p[i];
			if (i == m_first) {
				cout << " <--FIRST";
			}
			if (i == m_last) {
				cout << " <--LAST";
			}
			cout << endl;
		}
	}
	cout << endl;
	//size_t x = m_first;
	//for (const auto& val : *this)//������ ������������ ��������
	//{				
	//	cout << "index:" << x << " value: " << val << endl;
	//	x++;
	//}

}